home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.Draw / TRectObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  16.2 KB  |  538 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        TRectObj.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1992-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* See the files "=How to write your app" and "=Using TreeObj.c" for information
  20. ** on this function. */
  21.  
  22. /* This file implements the messages for the rect object. */
  23.  
  24.  
  25.  
  26. /*****************************************************************************/
  27.  
  28.  
  29.  
  30. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  31. #include "App.protos.h"        /* Get the prototypes for the application.        */
  32.  
  33. #ifndef __OSEVENTS__
  34. #include <OSEvents.h>
  35. #endif
  36.  
  37. #ifndef __OSUTILS__
  38. #include <OSUtils.h>
  39. #endif
  40.  
  41. #ifndef __QUICKDRAW__
  42. #include <Quickdraw.h>
  43. #endif
  44.  
  45. #ifndef __STRING__
  46. #include <String.h>
  47. #endif
  48.  
  49. #ifndef __TREEOBJ2__
  50. #include "TreeObj2.h"
  51. #endif
  52.  
  53. #ifndef __UTILITIES__
  54. #include "Utilities.h"
  55. #endif
  56.  
  57.  
  58.  
  59. /**********************************************************************/
  60.  
  61.  
  62.  
  63. RGBColor    gBorderColor  = {0, 0, 0};
  64. RGBColor    gContentColor = {0xFFFF, 0xFFFF, 0xFFFF};
  65. short        gPenHeight = 1;
  66. short        gPenWidth  = 1;
  67.  
  68.  
  69.  
  70. /**********************************************************************/
  71.  
  72.  
  73.  
  74. #pragma segment DrawObjects
  75. long    TRectObj(TreeObjHndl hndl, short message, long data)
  76. {
  77.     TreeObjHndl    root, shndl;
  78.     Rect        rct, oldRct, *rptr, grabber;
  79.     short        hit, fileRefNum, dx1, dx2, dy1, dy2, h, v, ptype;
  80.     short        fv, fh, flip, noDraw, w;
  81.     Point        where, begMouse, curMouse, offset;
  82.     Boolean        selected;
  83.     EventRecord    option;
  84.     ClickInfo    *click;
  85.     RgnHandle    rgn;
  86.     OSErr        err;
  87.     RGBColor    rgb, rgb2;
  88. #if VH_VERSION
  89.     char        *cptr;
  90. #endif
  91.  
  92.     switch (message) {
  93.         case INITMESSAGE:
  94.                 /* This is called either with a sub-message of CREATEINIT or WINDOWINIT.
  95.                 ** The sub-message CREATEINIT is sent in when the object is initially created.
  96.                 ** The sub-message WINDOWINIT is sent in when the object belongs to a document
  97.                 ** that was just assigned to a window.  Note that the object could be created
  98.                 ** into a document that already has a window.  In this case, only the
  99.                 ** CREATEINIT sub-message will be received.  It is the object's responsibility
  100.                 ** to determine if the document has a window when CREATEINIT is called.
  101.                 ** An additional possible sub-message is NOWINDOWINIT.  In this case, the
  102.                 ** document is being disconnected from a window, and therefore objects have
  103.                 ** to convert back to their non-window state.  Both the WINDOWINIT and
  104.                 ** NOWINDOWINIT sub-messages would be application-specific, and sending
  105.                 ** the sub-messages to the objects would be the application's responsibility. */
  106.             mDerefCommon(hndl)->penHeight    = gPenHeight;
  107.             mDerefCommon(hndl)->penWidth     = gPenWidth;
  108.             mDerefCommon(hndl)->borderColor  = gBorderColor;
  109.             mDerefCommon(hndl)->content      = true;
  110.             mDerefCommon(hndl)->contentColor = gContentColor;
  111.             break;
  112.  
  113.         case FREEMESSAGE:
  114.             if (mDerefCommon(hndl)->selected) {
  115.                 root = GetRootHndl(hndl);
  116.                 if ((*root)->type == ROOTOBJ)
  117.                     mDerefRoot(root)->numSelected--;
  118.                         /* Here the root object can be either the document root
  119.                         ** or the undo root.  The FREEOBJMESSAGE is due to the
  120.                         ** object being disposed of.  It may be being disposed of
  121.                         ** from either the document or undo side.  If it is being
  122.                         ** disposed of out of the undo side, we don't care about
  123.                         ** the selection status.  If it is being disposed of out
  124.                         ** of the document side, then we do care, as we will have
  125.                         ** one less selected object.  The numSelected count needs
  126.                         ** to reflect this. */
  127.             }
  128.             break;
  129.  
  130.         case COPYMESSAGE:
  131.                 /* CopyOneChild was called (indirectly) and an object has been
  132.                 ** cloned.  At this point the data area has already been copied.
  133.                 ** If the data area holds references to other handles, these
  134.                 ** handles need to be copied so that the object has its own copies.
  135.                 ** Since the data was copied from the original child, the fields
  136.                 ** holding references to handles actually contain the same reference
  137.                 ** that the original child contains.  To create unique handles for
  138.                 ** the copy, this object should pass itself an INITOBJMESSAGE to
  139.                 ** initially create unique handles for the copy.
  140.                 ** The data parameter for this message is the handle of the object
  141.                 ** that was copied.  It is possible that the copy won't be an exact
  142.                 ** copy, and that there should be some differences between the
  143.                 ** original and the copy.  By having a reference to the original,
  144.                 ** these situations can be resolved. */
  145.             break;
  146.  
  147.         case UNDOMESSAGE:
  148.             root  = GetRootHndl(hndl);
  149.             if ((*root)->type == ROOTOBJ) {
  150.                 ptype = (*((*hndl)->parent))->type;
  151.                 switch (data) {
  152.                     case UNDOFROMDOC:
  153.                         if (mDerefCommon(hndl)->selected == true) {
  154.                             mDerefCommon(hndl)->selected = false;
  155.                             mDerefRoot(root)->numSelected--;
  156.                         }        /* If a selected object is moving from the document into the */
  157.                         break;    /* undo, reflect this in the numSelected count in the root.  */
  158.  
  159.                     case UNDOTODOC:
  160.                         mDerefCommon(hndl)->selected = false;
  161.                         if (ptype != GROUPOBJ) {
  162.                             mDerefCommon(hndl)->selected = true;
  163.                             mDerefRoot(root)->numSelected++;
  164.                         }        /* An object moving into the document needs to be selected
  165.                                 ** so the user can see what changed.  The select status while
  166.                                 ** in the undo doesn't matter, as the user can't see what is in
  167.                                 ** the undo. */
  168.                         break;
  169.                 }
  170.             }
  171.             break;
  172.  
  173.         case CONVERTMESSAGE:
  174.                 /* This is called to convert any data that loses meaning when saved.
  175.                 ** It is called with a TOFILE or a FROMFILE sub-message.
  176.                 **
  177.                 ** For the CONVERTTOID sub-message:
  178.                 **
  179.                 ** If this object has any references to handles elsewhere in the document,
  180.                 ** these references lose meaning when saved to disk.  When the file is
  181.                 ** read in, the handle references will not coorelate to what is currently
  182.                 ** in ram.
  183.                 ** Prior to starting a file save, all of the objects in the tree are
  184.                 ** assigned sequential id numbers.  You can replace handle references
  185.                 ** with the 4-byte treeID.  To convert the handle to a treeID, just
  186.                 ** do something like the following:
  187.                 **     Hndl2ID(&DerefMyObject(hndl)->thingToConvert);
  188.                 **
  189.                 ** For the CONVERTTOHNDL sub-message:
  190.                 **
  191.                 ** Once the file is opened and completely loaded into ram, there may be
  192.                 ** objects that have references to elsewhere in the file that were stored
  193.                 ** as treeID's.  These need to be converted back to handle references.
  194.                 ** To do this, do something like the below:
  195.                 **      ID2Hndl(hndl, &DerefMyObject(hndl)->thingToConvert);
  196.                 */
  197.             break;
  198.  
  199.         case FREADMESSAGE:
  200.             fileRefNum = data;
  201.             err = ReadTreeObjData(hndl, fileRefNum);
  202.             mDerefCommon(hndl)->selected = false;
  203.             return(err);
  204.             break;
  205.  
  206.         case FWRITEMESSAGE:
  207.             fileRefNum = data;
  208.             return(WriteTreeObjData(hndl, fileRefNum));
  209.             break;
  210.  
  211.         case HREADMESSAGE:
  212.         case HWRITEMESSAGE:
  213.             break;
  214.  
  215.         case HITTESTMESSAGE:
  216.             click = (ClickInfo *)data;
  217.             where = click->localEvent.where;
  218.             hit   = 0;
  219.             switch (click->message) {
  220.                 case HITTESTOBJ:
  221.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  222.                     if (PtInRect(where, &rct)) {
  223.                         rgn = (RgnHandle)DoTreeObjMethod(hndl, GETRGNMESSAGE, 0);
  224.                         if (PtInRgn(where, rgn))
  225.                             hit = -1;
  226.                         DisposeRgn(rgn);
  227.                     }
  228.                     break;
  229.                 case HITTESTGRABBER:
  230.                     rct = mDerefCommon(hndl)->rect;
  231.                     if (mDerefCommon(hndl)->selected) {
  232.                         grabber = rct;
  233.                         InsetRect(&grabber, -4, -4);
  234.                         if (PtInRect(where, &grabber)) {
  235.                             GetMouse(&curMouse);
  236.                             for (;;) {
  237.                                 if (where.v < (rct.top + 4)) {
  238.                                     if (where.h <  (rct.left + 4) ) {
  239.                                         hit = 1;
  240.                                         flip = (VFLIPOBJ | HFLIPOBJ);
  241.                                         where.h  = rct.right;
  242.                                         where.v  = rct.bottom;
  243.                                         offset.h = rct.left - curMouse.h;
  244.                                         offset.v = rct.top  - curMouse.v;
  245.                                         break;
  246.                                     }
  247.                                     if (where.h >= (rct.right - 4)) {
  248.                                         hit = 2;
  249.                                         flip = VFLIPOBJ;
  250.                                         where.h  = rct.left;
  251.                                         where.v  = rct.bottom;
  252.                                         offset.h = rct.right - curMouse.h;
  253.                                         offset.v = rct.top   - curMouse.v;
  254.                                         break;
  255.                                     }
  256.                                 }
  257.                                 if (where.v >= (rct.bottom - 4)) {
  258.                                     if (where.h <  (rct.left + 4) ) {
  259.                                         hit = 3;
  260.                                         flip = HFLIPOBJ;
  261.                                         where.h  = rct.right;
  262.                                         where.v  = rct.top;
  263.                                         offset.h = rct.left   - curMouse.h;
  264.                                         offset.v = rct.bottom - curMouse.v;
  265.                                         break;
  266.                                     }
  267.                                     if (where.h >= (rct.right - 4)) {
  268.                                         hit = 4;
  269.                                         flip = 0;
  270.                                         where.h  = rct.left;
  271.                                         where.v  = rct.top;
  272.                                         offset.h = rct.right  - curMouse.h;
  273.                                         offset.v = rct.bottom - curMouse.v;
  274.                                         break;
  275.                                     }
  276.                                 }
  277.                                 break;
  278.                             }
  279.                         }
  280.                     }
  281.                     break;
  282.                 case CANBETARGET:
  283.                     return(false);
  284.                     break;
  285.                 case CANTAKEKEYS:
  286.                     return(false);
  287.                     break;
  288.             }
  289.             if (hit) {
  290.                 for (; (*(root = (*hndl)->parent))->type != ROOTOBJ; hndl = root);
  291.                     /* Group objects can not be hit directly.  Only the base
  292.                     ** objects can be hit.  However, if a base object is hit,
  293.                     ** we want to return the top-most group object that
  294.                     ** contains it.  Walk the tree up until the root object
  295.                     ** is hit.  The object below this is top-most group object,
  296.                     ** or the base object doesn't belong to a group. */
  297.                 if (click->message == HITTESTGRABBER) {
  298.                     if (mDerefRoot(root)->numSelected == 1) {
  299.                         click->localEvent.where = where;
  300.                         click->offset           = offset;
  301.                         click->oldFlip = click->newFlip = flip;
  302.                     }
  303.                     else hit = 0;
  304.                 }
  305.             }
  306.             if (!hit)
  307.                 hndl = nil;
  308.             click->grabber = hit;
  309.             return((long)hndl);
  310.             break;
  311.  
  312.         case GETRGNMESSAGE:
  313.             rgn = NewRgn();
  314.             rct = mDerefCommon(hndl)->rect;
  315.             RectRgn(rgn, &rct);
  316.             return((long)rgn);
  317.             break;
  318.  
  319.         case GETOBJRECTMESSAGE:
  320.         case GETBBOXMESSAGE:
  321.             rptr  = (Rect *)data;
  322.             *rptr = mDerefCommon(hndl)->rect;
  323.             break;
  324.  
  325.         case SETOBJRECTMESSAGE:
  326.             rptr = (Rect *)data;
  327.             mDerefCommon(hndl)->rect = *rptr;
  328.             break;
  329.  
  330.         case SECTOBJRECTMESSAGE:
  331.             rptr = (Rect *)data;
  332.             SectRect(rptr, &(mDerefCommon(hndl)->rect), &rct);
  333.             if (!EmptyRect(&rct)) return(true);
  334.             break;
  335.  
  336.         case DRAWMESSAGE:
  337.             rct = mDerefCommon(hndl)->rect;
  338.             h   = mDerefCommon(hndl)->penHeight;
  339.             w   = mDerefCommon(hndl)->penWidth;
  340.             PenSize(w, h);
  341.             switch (data) {
  342.                 case DRAWOBJ:
  343.                     if (gQDVersion)
  344.                         GetForeColor(&rgb);
  345.                     ForeColor(whiteColor);
  346.                     if (gQDVersion) {
  347.                         rgb2 = mDerefLine(hndl)->contentColor;
  348.                         RGBForeColor(&rgb2);
  349.                     }
  350.                     PaintRect(&rct);
  351.                     ForeColor(blackColor);
  352.                     if (gQDVersion) {
  353.                         rgb2 = mDerefLine(hndl)->borderColor;
  354.                         RGBForeColor(&rgb2);
  355.                     }
  356.                     FrameRect(&rct);
  357.                     if (gQDVersion)
  358.                         RGBForeColor(&rgb);
  359.                     break;
  360.                 case ERASEOBJ:
  361.                     EraseRect(&rct);
  362.                     break;
  363.                 case DRAWSELECT:
  364.                     if (mDerefCommon(hndl)->selected) {
  365.                         for (h = v = 1; (h > -1); h -= (v ^= 1)) {
  366.                             grabber.top  = v ? rct.top  : rct.bottom;
  367.                             grabber.left = h ? rct.left : rct.right;
  368.                             grabber.bottom = (grabber.top  -= 3) + 6;
  369.                             grabber.right  = (grabber.left -= 3) + 6;
  370.                             InvertRect(&grabber);
  371.                         }
  372.                     }
  373.                     break;
  374.                 case DRAWGHOST:
  375.                     PenMode(patXor);
  376.                     FrameRect(&rct);
  377.                     break;
  378.                 case DRAWMASK:
  379.                     FillRect(&rct, (ConstPatternParam)&qd.black);
  380.                     break;
  381.             }
  382.             PenNormal();
  383.             break;
  384.  
  385.         case PRINTMESSAGE:
  386.             DoTreeObjMethod(hndl, DRAWMESSAGE, DRAWOBJ);
  387.             break;
  388.  
  389. #if VH_VERSION
  390.         case VHMESSAGE:
  391.             cptr = ((VHFormatDataPtr)data)->data;
  392.             ccatchr(cptr, 13, 2);
  393.             ccat   (cptr, "$10: TRectObj:");
  394.             ccatchr(cptr, 13, 1);
  395.             ccat   (cptr, "  $00: selected     = ");
  396.             ccatdec(cptr, mDerefRect(hndl)->selected);
  397.             ccatchr(cptr, 13, 1);
  398.             rct = mDerefRect(hndl)->rect;
  399.             ccat   (cptr, "  $02: rect         = ($");
  400.             ccathex(cptr, 0, 4, 4, rct.top);
  401.             ccat   (cptr, ",$");
  402.             ccathex(cptr, 0, 4, 4, rct.left);
  403.             ccat   (cptr, ",$");
  404.             ccathex(cptr, 0, 4, 4, rct.bottom);
  405.             ccat   (cptr, ",$");
  406.             ccathex(cptr, 0, 4, 4, rct.right);
  407.             ccat   (cptr, ")");
  408.             ccatchr(cptr, 13, 1);
  409.             ccat   (cptr, "  $0A: penHeight    = ");
  410.             ccatdec(cptr, mDerefRect(hndl)->penHeight);
  411.             ccatchr(cptr, 13, 1);
  412.             ccat   (cptr, "  $0C: penWidth    = ");
  413.             ccatdec(cptr, mDerefRect(hndl)->penWidth);
  414.             ccatchr(cptr, 13, 1);
  415.             ccat   (cptr, "  $0E: borderColor  = ($");
  416.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.red);
  417.             ccat   (cptr, ",$");
  418.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.green);
  419.             ccat   (cptr, ",$");
  420.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.blue);
  421.             ccat   (cptr, ")");
  422.             ccatchr(cptr, 13, 1);
  423.             ccat   (cptr, "  $1$: content      = ($");
  424.             ccatdec(cptr, mDerefRect(hndl)->content);
  425.             ccatchr(cptr, 13, 1);
  426.             ccat   (cptr, "  $16: contentColor = ($");
  427.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.red);
  428.             ccat   (cptr, ",$");
  429.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.green);
  430.             ccat   (cptr, ",$");
  431.             ccathex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.blue);
  432.             ccat   (cptr, ")");
  433.             return(true);
  434.             break;
  435. #endif
  436.  
  437.         case COMPAREMESSAGE:
  438.             return(DefaultEqualTreeObjData(hndl, (TreeObjHndl)data));
  439.             break;
  440.  
  441. /* End of standard messages.  Start of object-specific messages. */
  442.  
  443.         case CLICKMESSAGE:
  444.             click = (ClickInfo *)data;
  445.             switch (click->message) {
  446.                 case CLICKSELECT:
  447.                     for (shndl = hndl; (*(root = (*shndl)->parent))->type != ROOTOBJ; shndl = root);
  448.                     selected = mDerefCommon(shndl)->selected;
  449.                     if ((!selected) || (click->localEvent.modifiers & shiftKey)) {
  450.                         if (shndl == hndl)
  451.                             DoTreeObjMethod(hndl, SETSELECTMESSAGE, SELECTTOGGLE);
  452.                                 /* If the object doesn't belong to a group, then we want to change
  453.                                 ** the select status.  If it belongs to a group, the group object
  454.                                 ** is responsible for maintaining the select status. */
  455.                     }
  456.                     break;
  457.                 case CLICKDRAG:
  458.                     OffsetRect(&(mDerefCommon(hndl)->rect), click->offset.h, click->offset.v);
  459.                     break;
  460.             }
  461.             break;
  462.  
  463.         case KEYMESSAGE:
  464.             break;
  465.  
  466.         case SETSELECTMESSAGE:
  467.             noDraw = (data & SELECTNODRAW);
  468.             data  ^= noDraw;
  469.  
  470.             selected = mDerefCommon(hndl)->selected;
  471.             if (selected != data) {        /* The select status is other than desired. */
  472.                 if (!noDraw) {
  473.                     mDerefCommon(hndl)->selected = true;
  474.                     DoTreeObjMethod(hndl, DRAWMESSAGE, DRAWSELECT);
  475.                         /* Force a draw-toggle of the selection grabbers. */
  476.                 }
  477.                 mDerefCommon(hndl)->selected = (selected ^= true);
  478.                 root = GetRootHndl(hndl);
  479.                 if (selected)
  480.                     mDerefRoot(root)->numSelected++;
  481.                 else
  482.                     mDerefRoot(root)->numSelected--;
  483.                         /* Change the global numSelected value to reflect the new
  484.                         ** number of selected objects. */
  485.             }
  486.             break;
  487.  
  488.         case GETSELECTMESSAGE:
  489.             return(mDerefCommon(hndl)->selected);
  490.             break;
  491.  
  492.         case SIZEMESSAGE:
  493.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  494.             click    = (ClickInfo *)data;
  495.             begMouse = click->localEvent.where;
  496.             GetMouse(&curMouse);
  497.             curMouse.h += click->offset.h;
  498.             curMouse.v += click->offset.v;
  499.             dx1 = dx2 = (curMouse.h - begMouse.h);
  500.             dy1 = dy2 = (curMouse.v - begMouse.v);
  501.             if (dx2 < 0)
  502.                 dx2 = -dx2;
  503.             if (dy2 < 0)
  504.                 dy2 = -dy2;
  505.             OSEventAvail(nullEvent, &option);
  506.             if ((*hndl)->type == EXTSELECTOBJ) option.modifiers = 0;
  507.             if (option.modifiers & shiftKey) {
  508.                 if (dx2 > dy2)
  509.                     dx2 = dy2;
  510.                 else
  511.                     dy2 = dx2;
  512.             }
  513.             if (dx1 < 0)
  514.                 dx2 = -dx2;
  515.             if (dy1 < 0)
  516.                 dy2 = -dy2;
  517.             curMouse.h = begMouse.h + dx2;
  518.             curMouse.v = begMouse.v + dy2;
  519.             fv = (curMouse.v < begMouse.v) ? VFLIPOBJ : 0x00;
  520.             fh = (curMouse.h < begMouse.h) ? HFLIPOBJ : 0x00;
  521.             rct.top    = (fv) ? curMouse.v : begMouse.v;
  522.             rct.bottom = (fv) ? begMouse.v : curMouse.v;
  523.             rct.left   = (fh) ? curMouse.h : begMouse.h;
  524.             rct.right  = (fh) ? begMouse.h : curMouse.h;
  525.             mDerefCommon(hndl)->rect = rct;
  526.             click->oldFlip = click->newFlip;
  527.             click->newFlip = (fv | fh);
  528.             return(!EqualRect(&rct, &oldRct));
  529.             break;
  530.  
  531.     }
  532.  
  533.     return(noErr);
  534. }
  535.  
  536.  
  537.  
  538.